package com.tonyodev.fetch2;

import ohos.app.Context;
import ohos.eventhandler.EventHandler;
import ohos.eventhandler.EventRunner;

import com.tonyodev.fetch2.database.FetchDatabaseManager;
import com.tonyodev.fetch2.exception.FetchException;
import com.tonyodev.fetch2.fetch.FetchHandler;
import com.tonyodev.fetch2.util.Defaults;
import com.tonyodev.fetch2core.DefaultStorageResolver;
import com.tonyodev.fetch2core.Downloader;
import com.tonyodev.fetch2core.FetchCoreDefaults;
import com.tonyodev.fetch2core.FetchCoreUtils;
import com.tonyodev.fetch2core.FetchLogger;
import com.tonyodev.fetch2core.FileServerDownloader;
import com.tonyodev.fetch2core.Logger;
import com.tonyodev.fetch2core.StorageResolver;

import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

/**
 * Class used to hold the configuration settings for a Fetch instance.
 *
 * @since 2021-05-20
 */
public class FetchConfiguration {
    @NotNull
    private final Context appContext;
    @NotNull
    private final String namespace;
    private final int concurrentLimit;
    private final long progressReportingIntervalMillis;
    private final boolean loggingEnabled;
    @NotNull
    private final Downloader httpDownloader;
    @NotNull
    private final NetworkType globalNetworkType;
    @NotNull
    private final Logger logger;
    private final boolean autoStart;
    private final boolean retryOnNetworkGain;
    @NotNull
    private final FileServerDownloader fileServerDownloader;
    private final boolean hashCheckingEnabled;
    private final boolean fileExistChecksEnabled;
    @NotNull
    private final StorageResolver storageResolver;
    @Nullable
    private final FetchNotificationManager fetchNotificationManager;

    private final FetchDatabaseManager fetchDatabaseManager;

    @Nullable
    private final EventHandler backgroundHandler;
    @NotNull
    private final PrioritySort prioritySort;
    @Nullable
    private final String internetCheckUrl;
    private final long activeDownloadsCheckInterval;
    private final boolean createFileOnEnqueue;
    private final int maxAutoRetryAttempts;
    private final boolean preAllocateFileOnCreation;
    @Nullable
    private final FetchHandler fetchHandler;

    /**
     * create instance
     *
     * @param appContext appContext
     * @param namespace namespace
     * @param concurrentLimit concurrentLimit
     * @param progressReportingIntervalMillis progressReportingIntervalMillis
     * @param loggingEnabled loggingEnabled
     * @param httpDownloader httpDownloader
     * @param globalNetworkType globalNetworkType
     * @param logger logger
     * @param autoStart autoStart
     * @param retryOnNetworkGain retryOnNetworkGain
     * @param fileServerDownloader fileServerDownloader
     * @param hashCheckingEnabled hashCheckingEnabled
     * @param fileExistChecksEnabled fileExistChecksEnabled
     * @param storageResolver storageResolver
     * @param fetchDatabaseManager fetchDatabaseManager
     * @param fetchNotificationManager fetchNotificationManager
     * @param backgroundHandler backgroundHandler
     * @param prioritySort prioritySort
     * @param internetCheckUrl internetCheckUrl
     * @param activeDownloadsCheckInterval activeDownloadsCheckInterval
     * @param createFileOnEnqueue createFileOnEnqueue
     * @param maxAutoRetryAttempts maxAutoRetryAttempts
     * @param preAllocateFileOnCreation preAllocateFileOnCreation
     * @param fetchHandler fetchHandler
     */
    public FetchConfiguration(@NotNull Context appContext,
                              @NotNull String namespace,
                              int concurrentLimit,
                              long progressReportingIntervalMillis,
                              boolean loggingEnabled,
                              @NotNull Downloader httpDownloader,
                              @NotNull NetworkType globalNetworkType,
                              @NotNull Logger logger,
                              boolean autoStart,
                              boolean retryOnNetworkGain,
                              @NotNull FileServerDownloader fileServerDownloader,
                              boolean hashCheckingEnabled,
                              boolean fileExistChecksEnabled,
                              @NotNull StorageResolver storageResolver,
                              FetchDatabaseManager fetchDatabaseManager,
                              @Nullable FetchNotificationManager fetchNotificationManager,
                              @Nullable EventHandler backgroundHandler,
                              @NotNull PrioritySort prioritySort,
                              @Nullable String internetCheckUrl,
                              long activeDownloadsCheckInterval,
                              boolean createFileOnEnqueue,
                              int maxAutoRetryAttempts,
                              boolean preAllocateFileOnCreation,
                              @Nullable FetchHandler fetchHandler) {
        this.appContext = appContext;
        this.namespace = namespace;
        this.concurrentLimit = concurrentLimit;
        this.progressReportingIntervalMillis = progressReportingIntervalMillis;
        this.loggingEnabled = loggingEnabled;
        this.httpDownloader = httpDownloader;
        this.globalNetworkType = globalNetworkType;
        this.logger = logger;
        this.autoStart = autoStart;
        this.retryOnNetworkGain = retryOnNetworkGain;
        this.fileServerDownloader = fileServerDownloader;
        this.hashCheckingEnabled = hashCheckingEnabled;
        this.fileExistChecksEnabled = fileExistChecksEnabled;
        this.storageResolver = storageResolver;
        this.fetchNotificationManager = fetchNotificationManager;
        this.backgroundHandler = backgroundHandler;
        this.prioritySort = prioritySort;
        this.internetCheckUrl = internetCheckUrl;
        this.activeDownloadsCheckInterval = activeDownloadsCheckInterval;
        this.createFileOnEnqueue = createFileOnEnqueue;
        this.maxAutoRetryAttempts = maxAutoRetryAttempts;
        this.preAllocateFileOnCreation = preAllocateFileOnCreation;
        this.fetchHandler = fetchHandler;
        this.fetchDatabaseManager = fetchDatabaseManager;
    }

    @NotNull
    public final Context getAppContext() {
        return this.appContext;
    }

    @NotNull
    public final String getNamespace() {
        return this.namespace;
    }

    public final int getConcurrentLimit() {
        return this.concurrentLimit;
    }

    public final long getProgressReportingIntervalMillis() {
        return this.progressReportingIntervalMillis;
    }

    public final boolean getLoggingEnabled() {
        return this.loggingEnabled;
    }

    @NotNull
    public final Downloader getHttpDownloader() {
        return this.httpDownloader;
    }

    @NotNull
    public final NetworkType getGlobalNetworkType() {
        return this.globalNetworkType;
    }

    @NotNull
    public final Logger getLogger() {
        return this.logger;
    }

    public final boolean getAutoStart() {
        return this.autoStart;
    }

    public final boolean getRetryOnNetworkGain() {
        return this.retryOnNetworkGain;
    }

    @NotNull
    public final FileServerDownloader getFileServerDownloader() {
        return this.fileServerDownloader;
    }

    public final boolean getHashCheckingEnabled() {
        return this.hashCheckingEnabled;
    }

    public final boolean getFileExistChecksEnabled() {
        return this.fileExistChecksEnabled;
    }

    @NotNull
    public final StorageResolver getStorageResolver() {
        return this.storageResolver;
    }

    @Nullable
    public final FetchNotificationManager getFetchNotificationManager() {
        return this.fetchNotificationManager;
    }

    @Nullable
    public final FetchDatabaseManager getFetchDatabaseManager() {
        return this.fetchDatabaseManager;
    }

    @Nullable
    public final EventHandler getBackgroundHandler() {
        return this.backgroundHandler;
    }

    @NotNull
    public final PrioritySort getPrioritySort() {
        return this.prioritySort;
    }

    @Nullable
    public final String getInternetCheckUrl() {
        return this.internetCheckUrl;
    }

    public final long getActiveDownloadsCheckInterval() {
        return this.activeDownloadsCheckInterval;
    }

    public final boolean getCreateFileOnEnqueue() {
        return this.createFileOnEnqueue;
    }

    public final int getMaxAutoRetryAttempts() {
        return this.maxAutoRetryAttempts;
    }

    public final boolean getPreAllocateFileOnCreation() {
        return this.preAllocateFileOnCreation;
    }

    @Nullable
    public final FetchHandler getFetchHandler() {
        return this.fetchHandler;
    }

    /**
     * Builder
     *
     * @since 2021-06-17
     */
    public static final class Builder {
        private final Context appContext;
        private String namespace;
        private int concurrentLimit;
        private long progressReportingIntervalMillis;
        private boolean loggingEnabled;
        private Downloader httpDownloader;
        private NetworkType globalNetworkType;
        private Logger logger;
        private boolean autoStart;
        private boolean retryOnNetworkGain;
        private FileServerDownloader fileServerDownloader;
        private boolean hashCheckEnabled;
        private boolean fileExistChecksEnabled;
        private StorageResolver storageResolver;
        private FetchNotificationManager fetchNotificationManager;
        private FetchDatabaseManager fetchDatabaseManager;
        private EventHandler backgroundHandler;
        private PrioritySort prioritySort;
        private String internetCheckUrl;
        private long activeDownloadCheckInterval;
        private boolean createFileOnEnqueue;
        private int maxAutoRetryAttempts;
        private boolean preAllocateFileOnCreation;
        private FetchHandler fetchHandler;

        /**
         * set namespace
         *
         * @param namespace namespace
         * @return namespace
         */
        @NotNull
        public final FetchConfiguration.Builder setNamespace(@Nullable String namespace) {
            FetchConfiguration.Builder var10000;
            String var10001;
            label17:
            {
                var10000 = this;
                if (namespace != null) {
                    CharSequence var2 = (CharSequence) namespace;
                    boolean var3 = false;
                    boolean var5 = var2.length() == 0;
                    if (!var5) {
                        var10001 = namespace;
                        break label17;
                    }
                }

                var10001 = "LibGlobalFetchLib";
            }

            var10000.namespace = var10001;
            return this;
        }

        /**
         * setNamespace$default
         *
         * @param var0 var0
         * @param var1 var1
         * @param var2 var2
         * @param var3 var3
         * @return FetchConfiguration.Builder
         */
        public static FetchConfiguration.Builder setNamespace$default(FetchConfiguration.Builder var0, String var1, int var2, Object var3) {
            if ((var2 & 1) != 0) {
                var1 = null;
            }
            return var0.setNamespace(var1);
        }

        /**
         * setHttpDownloader
         *
         * @param downloader downloader
         * @return FetchConfiguration.Builder
         */
        @NotNull
        public final FetchConfiguration.Builder setHttpDownloader(@NotNull Downloader downloader) {
            this.httpDownloader = downloader;
            return this;
        }

        /**
         * set fileServerDownloader
         *
         * @param fileServerDownloader
         * @return FetchConfiguration.Builder
         */
        @NotNull
        public final FetchConfiguration.Builder setFileServerDownloader(@NotNull FileServerDownloader fileServerDownloader) {
            this.fileServerDownloader = fileServerDownloader;
            return this;
        }

        /**
         * set progressReportingInterval
         *
         * @param progressReportingIntervalMillis progressReportingIntervalMillis
         * @return FetchConfiguration.Builder
         */
        @NotNull
        public final FetchConfiguration.Builder setProgressReportingInterval(long progressReportingIntervalMillis) {
            if (progressReportingIntervalMillis < 0L) {
                throw new FetchException("progressReportingIntervalMillis cannot be less than 0");
            } else {
                this.progressReportingIntervalMillis = progressReportingIntervalMillis;
                return this;
            }
        }

        /**
         * set downloadConcurrentLimit
         *
         * @param downloadConcurrentLimit downloadConcurrentLimit
         * @return FetchConfiguration.Builder
         */
        @NotNull
        public final FetchConfiguration.Builder setDownloadConcurrentLimit(int downloadConcurrentLimit) {
            if (downloadConcurrentLimit < 0) {
                throw (new FetchException("Concurrent limit cannot be less than 0"));
            } else {
                this.concurrentLimit = downloadConcurrentLimit;
                return this;
            }
        }

        /**
         * set globalNetworkType
         *
         * @param networkType networkType
         * @return FetchConfiguration.Builder
         */
        @NotNull
        public final FetchConfiguration.Builder setGlobalNetworkType(@NotNull NetworkType networkType) {
            this.globalNetworkType = networkType;
            return this;
        }

        /**
         * FetchConfiguration.Builder
         *
         * @param enabled enabled
         * @return FetchConfiguration.Builder
         */
        @NotNull
        public final FetchConfiguration.Builder enableLogging(boolean enabled) {
            this.loggingEnabled = enabled;
            return this;
        }

        /**
         * setLogger
         *
         * @param logger logger
         * @return FetchConfiguration.Builder
         */
        @NotNull
        public final FetchConfiguration.Builder setLogger(@NotNull Logger logger) {
            this.logger = logger;
            return this;
        }

        /**
         * enableAutoStart
         *
         * @param enabled enabled
         * @return FetchConfiguration.Builder
         */
        @NotNull
        public final FetchConfiguration.Builder enableAutoStart(boolean enabled) {
            this.autoStart = enabled;
            return this;
        }

        /**
         * enableRetryOnNetworkGain
         *
         * @param enabled enabled
         * @return FetchConfiguration.Builder
         */
        @NotNull
        public final FetchConfiguration.Builder enableRetryOnNetworkGain(boolean enabled) {
            this.retryOnNetworkGain = enabled;
            return this;
        }

        /**
         * enableHashCheck
         *
         * @param enabled enabled
         * @return FetchConfiguration.Builder
         */
        @NotNull
        public final FetchConfiguration.Builder enableHashCheck(boolean enabled) {
            this.hashCheckEnabled = enabled;
            return this;
        }

        /**
         * enableFileExistChecks
         *
         * @param enabled enabled
         * @return FetchConfiguration.Builder
         */
        @NotNull
        public final FetchConfiguration.Builder enableFileExistChecks(boolean enabled) {
            this.fileExistChecksEnabled = enabled;
            return this;
        }

        /**
         * setStorageResolver
         *
         * @param storageResolver storageResolver
         * @return FetchConfiguration.Builder
         */
        @NotNull
        public final FetchConfiguration.Builder setStorageResolver(@NotNull StorageResolver storageResolver) {
            this.storageResolver = storageResolver;
            return this;
        }

        /**
         * setNotificationManager
         *
         * @param fetchNotificationManager fetchNotificationManager
         * @return FetchConfiguration.Builder
         */
        @NotNull
        public final FetchConfiguration.Builder setNotificationManager(@Nullable FetchNotificationManager fetchNotificationManager) {
            this.fetchNotificationManager = fetchNotificationManager;
            return this;
        }

        /**
         * setDatabaseManager
         *
         * @param fetchDatabaseManager fetchDatabaseManager
         * @return FetchConfiguration.Builder
         */
        @NotNull
        public final FetchConfiguration.Builder setDatabaseManager(@Nullable FetchDatabaseManager fetchDatabaseManager) {
            this.fetchDatabaseManager = fetchDatabaseManager;
            return this;
        }

        /**
         * setBackgroundHandler
         *
         * @param handler handler
         * @return FetchConfiguration.Builder
         * @throws IllegalAccessException IllegalAccessException
         */
        @NotNull
        public final FetchConfiguration.Builder setBackgroundHandler(@NotNull EventHandler handler) throws IllegalAccessException {
            if (handler.getEventRunner().getThreadId() == EventRunner.getMainEventRunner().getThreadId()) {
                throw new IllegalAccessException("The background handler cannot use the main/ui thread");
            }
            this.backgroundHandler = handler;
            return this;
        }

        /**
         * setFetchHandler
         *
         * @param fetchHandler fetchHandler
         * @return FetchConfiguration.Builder
         */
        @NotNull
        public final FetchConfiguration.Builder setFetchHandler(@NotNull FetchHandler fetchHandler) {
            this.fetchHandler = fetchHandler;
            return this;
        }

        /**
         * setPrioritySort
         *
         * @param prioritySort prioritySort
         * @return FetchConfiguration.Builder
         */
        @NotNull
        public final FetchConfiguration.Builder setPrioritySort(@NotNull PrioritySort prioritySort) {
            this.prioritySort = prioritySort;
            return this;
        }

        /**
         * setInternetAccessUrlCheck
         *
         * @param url url
         * @return FetchConfiguration.Builder
         */
        @NotNull
        public final FetchConfiguration.Builder setInternetAccessUrlCheck(@Nullable String url) {
            this.internetCheckUrl = url;
            return this;
        }

        /**
         * setHasActiveDownloadsCheckInterval
         *
         * @param intervalInMillis intervalInMillis
         * @return FetchConfiguration.Builder
         */
        @NotNull
        public final FetchConfiguration.Builder setHasActiveDownloadsCheckInterval(long intervalInMillis) {
            if (intervalInMillis < 0L) {
                throw new FetchException("intervalInMillis cannot be less than 0");
            } else {
                this.activeDownloadCheckInterval = intervalInMillis;
                return this;
            }
        }

        /**
         * createDownloadFileOnEnqueue
         *
         * @param create create
         * @return FetchConfiguration.Builder
         */
        @NotNull
        public final FetchConfiguration.Builder createDownloadFileOnEnqueue(boolean create) {
            this.createFileOnEnqueue = create;
            return this;
        }

        /**
         * setAutoRetryMaxAttempts
         *
         * @param autoRetryMaxAttempts autoRetryMaxAttempts
         * @return FetchConfiguration.Builder
         */
        @NotNull
        public final FetchConfiguration.Builder setAutoRetryMaxAttempts(int autoRetryMaxAttempts) {
            if (autoRetryMaxAttempts < 0) {
                throw new IllegalArgumentException("The AutoRetryMaxAttempts has to be greater than -1");
            } else {
                this.maxAutoRetryAttempts = autoRetryMaxAttempts;
                return this;
            }
        }

        /**
         * preAllocateFileOnCreation
         *
         * @param preAllocateFile preAllocateFile
         * @return FetchConfiguration.Builder
         */
        @NotNull
        public final FetchConfiguration.Builder preAllocateFileOnCreation(boolean preAllocateFile) {
            this.preAllocateFileOnCreation = preAllocateFile;
            return this;
        }

        /**
         * create instance
         *
         * @return FetchConfiguration
         */
        @NotNull
        public final FetchConfiguration build() {
            Logger prefsLogger = logger;
            if (prefsLogger instanceof FetchLogger) {
                prefsLogger.setEnabled(loggingEnabled);
                if (((FetchLogger) prefsLogger).getTag().equals(FetchCoreDefaults.DEFAULT_TAG)) {
                    ((FetchLogger) prefsLogger).setTag(namespace);
                }
            } else {
                logger.setEnabled(loggingEnabled);
            }
            return new FetchConfiguration(
                appContext,
                this.namespace,
                this.concurrentLimit,
                this.progressReportingIntervalMillis,
                this.loggingEnabled,
                this.httpDownloader,
                this.globalNetworkType, prefsLogger,
                this.autoStart,
                this.retryOnNetworkGain,
                this.fileServerDownloader,
                this.hashCheckEnabled,
                this.fileExistChecksEnabled,
                this.storageResolver,
                this.fetchDatabaseManager,
                this.fetchNotificationManager,
                this.backgroundHandler,
                this.prioritySort,
                this.internetCheckUrl,
                this.activeDownloadCheckInterval,
                this.createFileOnEnqueue,
                this.maxAutoRetryAttempts,
                this.preAllocateFileOnCreation,
                this.fetchHandler);
        }

        /**
         * build instance
         *
         * @param context context
         */
        public Builder(@NotNull Context context) {
            super();
            this.loggingEnabled = FetchCoreDefaults.DEFAULT_LOGGING_ENABLED;
            this.appContext = context.getApplicationContext();
            this.namespace = Defaults.DEFAULT_INSTANCE_NAMESPACE;
            this.concurrentLimit = Defaults.DEFAULT_CONCURRENT_LIMIT;
            this.progressReportingIntervalMillis = FetchCoreDefaults.DEFAULT_PROGRESS_REPORTING_INTERVAL_IN_MILLISECONDS;
            this.httpDownloader = Defaults.getDefaultDownloader();
            this.globalNetworkType = Defaults.getDefaultGlobalNetworkType();
            this.logger = Defaults.getDefaultLogger();
            this.autoStart = Defaults.DEFAULT_AUTO_START;
            this.retryOnNetworkGain = Defaults.DEFAULT_RETRY_ON_NETWORK_GAIN;
            this.fileServerDownloader = Defaults.getDefaultFileServerDownloader();
            this.hashCheckEnabled = Defaults.DEFAULT_HASH_CHECK_ENABLED;
            this.fileExistChecksEnabled = Defaults.DEFAULT_FILE_EXIST_CHECKS;
            this.storageResolver = new DefaultStorageResolver(appContext, FetchCoreUtils.getFileTempDir(appContext));
            this.prioritySort = Defaults.getDefaultPrioritySort();
            this.activeDownloadCheckInterval = Defaults.DEFAULT_HAS_ACTIVE_DOWNLOADS_INTERVAL_IN_MILLISECONDS;
            this.createFileOnEnqueue = Defaults.DEFAULT_CREATE_FILE_ON_ENQUEUE;
            this.maxAutoRetryAttempts = Defaults.DEFAULT_GLOBAL_AUTO_RETRY_ATTEMPTS;
            this.preAllocateFileOnCreation = Defaults.DEFAULT_PREALLOCATE_FILE_ON_CREATE;
        }
    }
}
